﻿using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using StepsManagerLibrary.Annotations;
using StepsManagerLibrary.Interfaces;

namespace StepsManagerLibrary
{
    public abstract class BaseStepManager<T> : IStepManager<T> where T : class 
    {
        #region Fields

        protected IList<T> _steps;
        protected T _currentStep;
        protected T _sourceStep;
        protected T _destinationStep;

        #endregion

        #region Constructors

        /// <summary>
        /// Initialize the StepManager
        /// </summary>
        protected BaseStepManager()
        {
            _steps = new ObservableCollection<T>();
        }

        #endregion

        #region IStepManager<T> Members

        #region INotify Properties

        /// <summary>
        /// Generate an IList with the ability to notify that it is being updated
        /// </summary>
        /// <param name="stepElements">
        /// Collection to convert into an ObservableCollection
        /// </param>
        /// <returns>ObservableCollection of Steps</returns>
        protected virtual IList<T> GenerateObservableCollectionOfSteps(IList<T> stepElements)
        {
            IList<T> result = new ObservableCollection<T>();

            if (stepElements != null)
            {
                foreach (T element in stepElements)
                {
                    result.Add(element);
                }
            }

            return result;
        }

        /// <summary>
        /// Notifying property of IStepManager.Steps
        /// </summary>
        public virtual IList<T> Steps
        {
            get { return _steps; }
            set
            {
                if (_steps != value || _steps == null)
                {
                    _steps = GenerateObservableCollectionOfSteps(value);

                    if (_steps.Count > 0)
                        _currentStep = _steps[0];

                    OnPropertyChanged("Steps");
                    OnPropertyChanged("CurrentStep");
                }
            }
        }

        /// <summary>
        /// Notifying property of IStepManager.SourceStep
        /// </summary>
        public virtual T SourceStep
        {
            get { return _sourceStep; }
            protected set
            {
                if (_sourceStep != value)
                {
                    _sourceStep = value;
                    OnPropertyChanged("SourceStep");
                }
            }
        }

        /// <summary>
        /// Notifying property of IStepManager.CurrentStep
        /// </summary>
        public virtual T CurrentStep
        {
            get { return _currentStep; }
            protected set
            {
                if (_currentStep != value)
                {
                    _currentStep = value;
                    OnPropertyChanged("CurrentStep");
                }
            }
        }

        /// <summary>
        /// Notifying property of IStepManager.DestinationStep
        /// </summary>
        public virtual T DestinationStep
        {
            get { return _destinationStep; }
            protected set
            {
                if (_destinationStep != value)
                {
                    _destinationStep = value;
                    OnPropertyChanged("DestinationStep");
                }
            }
        }

        #endregion

        #region Steps List

        /// <summary>
        /// Access the Step at the specific position
        /// </summary>
        /// <param name="i">Position</param>
        /// <returns>Step at the position</returns>
        public virtual T this[int i]
        {
            get
            {
                if (_steps == null || i >= _steps.Count)
                    return null;

                return _steps[i];
            }
        }

        /// <summary>
        /// Add a Step at the end of the current collection of steps
        /// </summary>
        /// <param name="step">Step to be added</param>
        /// <returns>The position of the added step</returns>
        public virtual int AddStep(T step)
        {
            if (_steps == null || _steps.Contains(step))
                return -1;

            _steps.Add(step);

            if (_steps.Count == 1)
            {
                CurrentStep = step;
            }

            return _steps.Count - 1;
        }

        /// <summary>
        /// Insert a step at a specific position in the list
        /// </summary>
        /// <param name="step">Step to be added</param>
        /// <param name="pos">Position to add the step</param>
        /// <returns>The step has been added correctly</returns>
        public virtual bool InsertStepAt(T step, int pos)
        {
            if (_steps == null || _steps.Contains(step) ||
                pos > _steps.Count || pos < 0)
                return false;

            _steps.Insert(pos, step);

            return true;
        }

        /// <summary>
        /// If the step provided exist in the collection
        /// removes the step from the collection
        /// </summary>
        /// <param name="step">Step to be removed</param>
        /// <returns>The step has been found and removed</returns>
        public virtual bool RemoveStep(T step)
        {
            if (_steps == null || !_steps.Contains(step))
                return false;

            _steps.Remove(step);
            return true;
        }

        #endregion

        #region Navigation
        public abstract bool GoToFirst();
        public abstract bool GoToLast();
        public abstract bool GoToPrevious();
        public abstract bool GoToNext();
        public abstract bool GoTo(T step);
        #endregion 
        
        #endregion

        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion

        #region Methods

        /// <summary>
        /// Check whether a Move operation can be started
        /// </summary>
        /// <returns></returns>
        protected virtual bool CanDoAMoveOperation()
        {
            return _steps != null && _steps.Any() &&
                   _sourceStep == null && _destinationStep == null;
        }

        #endregion
    }
}
